#!/bin/bash
set -e
source ./variable.sh
podnetwork=10.244.0.0/16
runtime_type=docker
usetemprepo=0
ip=$(hostname -I | awk '{print $1}')
name=$(hostname)
install_para=
ctrd_install_para=

help() {
    echo "Usage:"
    echo "Install: $0 <-i ONLYINSTALL> <-d DIST> <-b BASELINE>"
    echo "Setup: $0 <-d DIST> <-b BASELINE> <-n NODETYPE> <-t CONTAINERTYPE> <-p NETPLUGIN>"
    echo "Other options: $0 [-c] [-h]"
    echo "Description:"
    echo "without -i will install and setup current node with -d -b -n -t and -p"
    echo "-c reset and clean current node"
    echo "-h print help message"
    echo "-i ONLYINSTALL package docker or k8s, without any config"
    echo "-d DIST, e.g. ctl2 or ctl3 or ctl4 or oe2309 or oe2403 or oe2503"
    echo "-n NODETYPE, e.g. master or worker"
    echo "-b BASELINE, e.g. 120 or 125 or 129"
    echo "-t CONTAINERTYPE, e.g. docker or containerd or crio"
    echo "-p NETPLUGIN, e.g. flannel or calico"
    exit -1
}

install_package() {
    local package=$1
    local installparam=$2
    local dist=$3
    echo "执行: yum install -y ${package} ${installparam}"
    if [[ $dist == oe* ]];then
        yum install -y "${package}" ${installparam} # for oula
    else
        local version_key=$(echo "${package^^}" | tr '-' '_')_VERSION
        local version=${!version_key}
        if [[ -z "$version" ]]; then
            echo "版本未定义或为空: $version_key"
            return 1
        fi
        if rpm -q "${package}-${version}.*" > /dev/null 2>&1; then
            echo "${package}-${version} 已安装"
            return 0
        fi
        yum install -y "${package}-${version}.$dist"  # for ctyunos 
    fi
}

change_baseline() {
  if [[ $baseline == "120" && $dist != ct* ]]; then
    echo "切换至120源"
    cp config/openEuler2203.repo /etc/yum.repos.d/
    install_para="--disablerepo=* --enablerepo=everything-2203 --enablerepo=update-2203 --enablerepo=EPOL-2203"
    ctrd_install_para=$install_para
    yum_repo="openEuler2203.repo"
    usetemprepo=1
  elif [[ $baseline == "125" && $dist != ct* ]]; then
    echo "切换至125基线源.."
    cp config/openEuler2309.repo  /etc/yum.repos.d/ --update
    #添加129源（使用特定containerd版本）
    cp config/openEuler2403.repo /etc/yum.repos.d/ --update
    install_para="--disablerepo=* --enablerepo=OS-2309 --enablerepo=everything-2309 --enablerepo=EPOL-2309"
    ctrd_install_para="--disablerepo=* --enablerepo=OS-2403 --enablerepo=everything-2403  --enablerepo=update-2403"
    yum_repo="openEuler2*"
    usetemprepo=1
  elif [[ $baseline == "129" && $dist != "oe2403" && $dist != ct* ]]; then
    echo "使用129基线源"
    cp config/openEuler2403.repo /etc/yum.repos.d/ --update
    install_para="--disablerepo=* --enablerepo=OS-2403 --enablerepo=everything-2403 --enablerepo=EPOL-2403 --enablerepo=update-2403"
    yum_repo="openEuler2403.repo"
    ctrd_install_para="--disablerepo=* --enablerepo=OS-2403 --enablerepo=everything-2403  --enablerepo=update-2403"
    usetemprepo=1
  fi
  yum clean all
  yum makecache || (echo "无法连接yum源，请将所有依赖rpm手动安装" && exit 1)
}

# 检查镜像是否存在
check_image_exists() {
    local image_name=$1
    
    case "$runtime_type" in
        "containerd")
            ctr -n k8s.io images list -q | grep -q "^${image_name}$"
            ;;
        "crio")
            crictl images --quiet | xargs -I {} crictl inspecti {} | grep -q "\"${image_name}\""
            ;;
        "docker")
            docker images --format "{{.Repository}}:{{.Tag}}" | grep -q "^${image_name}$"
            ;;
        *)
            return 1
            ;;
    esac
}

# 静默拉取镜像
pull_image_if_not_exists() {
    local full_image=$1  # 现在接收的是完整镜像地址
    
    # 检查镜像是否已存在
    if check_image_exists "$full_image"; then
        echo "镜像 $full_image 已存在，跳过拉取"
        return 0
    fi
    
    echo "正在拉取镜像: $full_image"

     case "$runtime_type" in
        "containerd")
            ctr -n k8s.io image pull "$full_image"
            ;;
        "crio")
            crictl pull "$full_image"
            ;;
        "docker")
            docker pull "$full_image"
            ;;
        *)
            echo "错误: 未知的容器运行时 $runtime_type"
            return 1
            ;;
    esac
    
    # 检查拉取是否成功
    if check_image_exists "$full_image"; then
        echo "镜像 $full_image 拉取成功"
        return 0
    else
        echo "错误: 镜像 $full_image 拉取失败"
        return 1
    fi
}

pull_required_images() {
    local netplugin=$1
    
    echo "使用网络插件: $netplugin"
    
    if [[ "$netplugin" == "calico" ]]; then
        for image_name in "${!calico_images[@]}"; do
            version_var="${calico_images[$image_name]}"
            version="${!version_var}"
            
            if [[ "$baseline" != "120" && "$image_name" == "pod2daemon-flexvol" ]]; then
                continue
            fi
            
            # 使用环境变量构建镜像地址
            full_image="${CALICO_REPO}/${image_name}:${version}"
            echo "处理Calico镜像: $full_image"
            pull_image_if_not_exists "$full_image"
        done
    else
        for image_name in "${!flannel_images[@]}"; do
            version_var="${flannel_images[$image_name]}"
            version="${!version_var}"
            
            # 使用环境变量构建镜像地址
            full_image="${IMAGE_REPO}/${image_name}:${version}"
            echo "处理Flannel镜像: $full_image"
            pull_image_if_not_exists "$full_image"
        done
    fi
    
    # 拉取K8s核心镜像
    for image_name in "${!k8s_images[@]}"; do
        version_var="${k8s_images[$image_name]}"
        version="${!version_var}"
        
        # 使用环境变量构建镜像地址
        full_image="${IMAGE_REPO}/${image_name}:${version}"
        echo "处理K8S镜像: $full_image"
        pull_image_if_not_exists "$full_image"
    done
    
    echo "镜像拉取阶段完成"
}

deploy_network_plugin_local() {
    echo "在本地节点部署网络插件: $netplugin"
    
    if [[ "$netplugin" == "calico" ]]; then
        deploy_calico_local
    else
        deploy_flannel_local
    fi
}

deploy_calico_local() {
    echo "部署Calico网络插件到本地节点"
    local calico_yaml="config/calico-${CALICO_VERSION}.yaml"
    
    if [[ -f "$calico_yaml" ]]; then
        echo "找到本地Calico配置文件，直接应用"
        sed "s/ARCH/$(arch)/g" "$calico_yaml" | kubectl apply -f -
        
        if [[ $? -eq 0 ]]; then
            echo "Calico网络插件部署成功"
        else
            echo "错误：Calico网络插件部署失败"
            return 1
        fi
    else
        echo "错误：本地Calico配置文件不存在: $calico_yaml"
        return 1
    fi
}

# 本地部署Flannel
deploy_flannel_local() {
    echo "部署Flannel网络插件到本地节点"
    local flannel_yaml="config/flannel.yaml"
    
    if [[ -f "$flannel_yaml" ]]; then
        echo "找到本地Flannel配置文件，直接应用"
        sed "s/ARCH/$(arch)/g" "$flannel_yaml" | kubectl apply -f -
        
        if [[ $? -eq 0 ]]; then
            echo "Flannel网络插件部署成功"
        else
            echo "错误：Flannel网络插件部署失败"
            return 1
        fi
    else
        echo "错误：本地Flannel配置文件不存在: $flannel_yaml"
        return 1
    fi
}

setup_kubectl_completion() {
    echo "配置kubectl bash补全功能..."
    yum install -y bash-completion
    # 加载bash-completion
    source /usr/share/bash-completion/bash_completion 2>/dev/null || true
    # 设置kubectl补全
    if command -v kubectl &> /dev/null; then
        source <(kubectl completion bash) 2>/dev/null || true
        # 添加到.bashrc中，使其持久化
        if ! grep -q "kubectl completion bash" ~/.bashrc 2>/dev/null; then
            echo 'source <(kubectl completion bash)' >> ~/.bashrc
            echo "kubectl bash补全已添加到~/.bashrc"
        else
            echo "kubectl bash补全已存在于~/.bashrc中"
        fi
        echo "kubectl bash补全配置完成"
    fi
}

#Main
[[ $# == 0 ]] && help && exit
[[ ! -d config ]] && echo "请切换到源码目录或/etc/k8s-install目录运行" && exit
while getopts 'i:d:n:t:b:p:uch' OPT; do
    case $OPT in
        i) if [ "$OPTARG" == "docker" ] || [ "$OPTARG" == "k8s" ];then
               onlyinstall=$OPTARG && echo "only install $OPTARG..."
           else
               echo "unsupport onlyinstall value $OPTARG..." && exit
           fi
        ;;
        d) if [[ "$OPTARG" =~ ^(ctl2|ctl3|ctl4|oe2403|oe2309|oe2503)$ ]];then
               dist=$OPTARG && echo "dist is set as $OPTARG..."
           else
               echo "unsupport dist type $OPTARG..." && exit
           fi
        ;;
        n) if [ "$OPTARG" == "master" ] || [ "$OPTARG" == "worker" ];then
               nodetype=$OPTARG && echo "nodetype is set as $OPTARG..."
           else
               echo "unsupport node type $OPTARG..." && exit
           fi
        ;;
        t) if [ "$OPTARG" == "docker" ] || [ "$OPTARG" == "containerd" ] || [ "$OPTARG" == "crio" ];then
               runtime_type=$OPTARG && echo "runtime_type is set as $OPTARG..."
           else
               echo "unsupport container engine type $OPTARG..." && exit
           fi
        ;;
        p)  if [[ "$OPTARG" == "flannel" || "$OPTARG" == "calico" ]]; then
                netplugin=$OPTARG
                echo "netplugin is set as $OPTARG..."
            else
                echo "unsupported network plugin $OPTARG..."
                exit
            fi
        ;;
        u) for package in $online_install_pkg; do 
            if rpm -q "$package" > /dev/null 2>&1; then 
                yum update -y "$package" --obsoletes
            else 
                echo "Package $package is not installed, skipping update."
            fi
           done
           exit 0
           ;;
        b)  case "$OPTARG" in
                "120"|"125"|"129")
                baseline=$OPTARG && echo "baseline is set as $OPTARG.."
		        set_version_${OPTARG}
                ;;
            *)
                echo "Invalid version specified! ?${OPTARG}?"
                exit 1 
                ;;
            esac
            ;;
        c) echo "clean k8s setup..."
           kubeadm reset -f
           rm -rf /etc/cni/*
           ifconfig cni0 down > /dev/null 2>&1
           ifconfig flannel.1 down > /dev/null 2>&1
           ifconfig docker0 down > /dev/null 2>&1
           ip link delete cni0 > /dev/null 2>&1
           ip link delete flannel.1 > /dev/null 2>&1
           echo "k8s reset and network all deleted!" && exit
        ;;
        h) help && exit;;
        ?) help && exit;;
    esac
done

# 检查是否指定了 -d 参数
if [ -z "$dist" ]; then
    echo "Error: -d DIST parameter is required."
    help    
fi

if [[ $dist == "ctl4" || $dist == "oe2403" || $dist == "oe2503" ]]; then
    if [[ $baseline != "129" ]]; then
        echo "警告：$dist 发行版必须使用129基线，已忽略指定的基线版本$baseline，并设置为129基线。"
        baseline=129
        set_version_129  # 确保调用正确的版本设置函数
    fi
fi

if [[ $baseline != "120" && "x$runtime_type" == "xdocker" && "x$onlyinstall" != "xdocker" ]]; then
    echo "${baseline}基线暂不支持使用docker部署k8s,该基线只能用-t containerd部署k8s; 仅安装docker请使用 -i docker"
    exit
fi

echo "开始一键安装模式，该过程需要使用root"
[ `whoami` == "root" ] || exit
echo -e "\033[44;37m 安装基础库 \033[0m"

yum install -y libcgroup container-selinux 

if [[ $dist == oe2503 && $baseline != "129" ]] || [[ $dist == oe2403 && $baseline != "129" ]] || [[ $dist == oe2303 && $baseline != "125" ]] || [[ $dist == oe2203 && $baseline != "120" ]];then
    change_baseline
fi

#grep docker.ctyun.cn /etc/hosts || echo "124.236.120.248 docker.ctyun.cn" >> /etc/hosts
echo -e "\033[44;37m 安装 runc/containerd/docker \033[0m"
install_package  runc "${install_para}" "$dist"
install_package  containerd "${ctrd_install_para}" "$dist"
if [ "x$runtime_type" == "xcontainerd" ];then
    mkdir -p /etc/containerd && containerd config default | sudo tee /etc/containerd/config.toml > /dev/null 2>&1 
    if [ $baseline != "120" ]; then
        sed -i "s#sandbox_image = \"registry.k8s.io/pause:.*\"#sandbox_image = \"${IMAGE_REPO}/pause:${PAUSE_VERSION}\"#" /etc/containerd/config.toml
        sed -i 's#SystemdCgroup = false#SystemdCgroup = true#' /etc/containerd/config.toml 
    else
        sed -i "s#sandbox_image = \"k8s.gcr.io/pause:.*\"#sandbox_image = \"${IMAGE_REPO}/pause:${PAUSE_VERSION}\"#"  /etc/containerd/config.toml
    fi
    systemctl enable containerd
    systemctl restart containerd.service

    sockoption="--cri-socket /run/containerd/containerd.sock"
    sedcmd='-e "s,dockershim.sock,containerd/containerd.sock,g"'

    imagecmd="ctr -n k8s.io image pull ${IMAGE_REPO}"
    
fi

if [[ "x$runtime_type" == "xdocker" || "x$onlyinstall" == "xdocker" ]];then
    if [[ $dist == ct* ]] && [[  $baseline == "120" ]]; then
        dockername="docker"
    else
        dockername="moby"
    fi
    if which docker > /dev/null 2>&1; then
        [ $(rpm -q $(rpm -qf $(which docker)) --qf %{version} | awk -F '.' '{print $1}') -le 18 ] && \
        (rpm -e runc --nodeps; yum install -y runc;)
    else
        install_package ${dockername} "${install_para}" "$dist"
    fi

    if [[ $baseline == "120" && $dist == ct* ]]; then
        install_package "docker-cli" "${install_para}" "$dist"
    fi    

    if [ ! -f /etc/docker/daemon.json ];then
        mkdir -p /etc/docker
        cp config/daemon.json /etc/docker/daemon.json
        if [ $baseline == "125" ]; then
            cp /etc/docker/daemon.json /etc/docker/daemon.conf
        fi
    fi
    #sleep 15
    systemctl enable docker
    systemctl daemon-reload
    systemctl restart docker
    imagecmd="docker pull ${IMAGE_REPO}"
    echo "Docker 安装完成."
fi

if [[ "$runtime_type" == "crio" ]]; then
    install_package  crio "${install_para}" "$dist"
    if [[ ($dist == "ctl4" || $dist == "oe2403" || $dist == "oe2503") && "$baseline" == "129" ]]; then
        if [[ -f /etc/crio/crio.conf ]]; then
            sed -i "s|^#\s*pause_image\s*=\s*\".*\"|pause_image = \"${IMAGE_REPO}/pause:${PAUSE_VERSION}\"|" /etc/crio/crio.conf
        fi
        systemctl enable crio
        systemctl restart crio.service
        sockoption="--cri-socket /var/run/crio/crio.sock"
        imagecmd="crictl pull ${IMAGE_REPO}"
    else
        echo "不支持在$dist上使用CRI-O部署"
        exit
    fi
fi

if [ "x$onlyinstall" == "xdocker" ];then
    exit
fi

echo -e "\033[44;37m 安装 k8s rpms \033[0m"
yum install -y socat conntrack-tools
echo "依赖安装完毕!"

if [ "$nodetype" == "worker" ];then
    install_package kubernetes-kubelet "${install_para}" "$dist"
    install_package kubernetes-client "${install_para}" "$dist"
    install_package kubernetes-kubeadm "${install_para}" "$dist"
    install_package kubernetes-node "${install_para}" "$dist"
elif [ "$nodetype" == "master" ];then
    install_package kubernetes-client "${install_para}" "$dist"
    install_package kubernetes-kubeadm "${install_para}" "$dist"
    install_package kubernetes-master "${install_para}" "$dist"
    install_package kubernetes-kubelet "${install_para}" "$dist"
    install_package kubernetes-node "${install_para}" "$dist"
else
    echo "Please specify the 'nodetype' correctly through the '-n' option." && exit
fi

systemctl enable kubelet

if [ ! -d /opt/cni ];then
    echo -e "\033[44;37m 安装k8s cni插件 \033[0m"
    yum install -y containernetworking-plugins 
    mkdir -p /opt/cni
    ln -s /usr/libexec/cni /opt/cni/bin
fi

echo -e "\033[44;37m 安装crictl \033[0m"
install_package cri-tools "${install_para}" "$dist"



if [ $usetemprepo == 1 ]; then
    echo "移除临时源"
    rm -f /etc/yum.repos.d/${yum_repo}
fi


if [ ! -f /etc/kubernetes/admin.conf ];then
    echo -e "\033[44;37m 设置k8s \033[0m"
    iptables -P FORWARD ACCEPT
    systemctl stop firewalld.service
    systemctl disable firewalld.service
    sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
    swapoff -a
    modprobe br_netfilter
    sed -i 's/net.ipv4.ip_forward=0/net.ipv4.ip_forward=1/g' /etc/sysctl.conf
    sysctl --system > /dev/null 2>&1
    grep ^nameserver /etc/resolv.conf || (chmod a+w /etc/resolv.conf && echo "nameserver 114.114.114.114" >> /etc/resolv.conf)
    pull_required_images "$netplugin"
    
    if [ "x$onlyinstall" == "xk8s" ]; then
        echo "rpm和镜像已安装完成！" 
        exit   
    fi

    if [ "$nodetype" == "master" ];then
        if [[ "$runtime_type" == "crio" && ( $dist == oe2403 || $dist == oe2503 ) ]]; then
       
            # 生成kubeadm配置文件
            cp config/kubeadm-template.yaml config/kubeadm.yaml
            sed -i -e "s/advertiseAddress: .*/advertiseAddress: $ip/g" \
            -e "s/name: .*/name: $name/g" \
            -e "s|imageRepository:.*|imageRepository: ${IMAGE_REPO}|g" \
            -e "s|podSubnet:.*|podSubnet: ${podnetwork}|g" \
            -e "s|kubernetesVersion:.*|kubernetesVersion: ${KUBERNETES_VERSION}|g" \
            -e "s|criSocket:.*|criSocket: unix:///var/run/crio/crio.sock|g" \
            config/kubeadm.yaml
        
        init_cmd="kubeadm init --config config/kubeadm.yaml"
        else
            # 使用命令行参数
            case "$runtime_type" in
                "containerd") socket_option="--cri-socket /run/containerd/containerd.sock" ;;
                "crio") socket_option="--cri-socket /var/run/crio/crio.sock" ;;
            esac
            
            init_cmd="kubeadm init $socket_option --image-repository=${IMAGE_REPO} --pod-network-cidr=$podnetwork --apiserver-advertise-address=$ip --kubernetes-version=${KUBERNETES_VERSION}"
        fi
        
        # 执行初始化
        echo "执行命令: $init_cmd"
        $init_cmd || {
            echo "错误：master节点初始化失败"
            exit 1
        }
        
        #默认使用参数配置（因为可做到无人工修改），也可以解注如下代码并手工修改kubeadm-template.yaml，通过config方式完成init。
        #cp config/kubeadm-template.yaml kubeadm.yaml
        #sed -i -e "s/advertiseAddress: ip/advertiseAddress: $ip/g" -e "s/name: master/name: $name/g" -e "s/ARCH/`arch`/g" $sedcmd kubeadm.yaml
        #kubeadm init --config kubeadm.yaml
        mkdir -p $HOME/.kube
        cp /etc/kubernetes/admin.conf $HOME/.kube/config
        cp /etc/kubernetes/admin.conf ~/.kube/config
       	chown $(id -u):$(id -g) $HOME/.kube/config
        deploy_network_plugin_local
        sed -i "/--trusted-ca-file=/a \    $etcd_insert_content" /etc/kubernetes/manifests/etcd.yaml
        sed -i "/--tls-private-key-file=/a \    $kube_apiserver_insert_content" /etc/kubernetes/manifests/kube-apiserver.yaml
        sed -i "/--use-service-account-credentials=true/a \    $kube_controller_insert_content" /etc/kubernetes/manifests/kube-controller-manager.yaml
        sed -i "/--leader-elect=true/a \    $kube_scheduler_insert_content" /etc/kubernetes/manifests/kube-scheduler.yaml
        sed -i "$ a\\$kubelet_insert_content" /var/lib/kubelet/config.yaml
        systemctl restart kubelet
        sleep 5
        echo -e "\033[44;37m 去除k8s污点 \033[0m"
        export KUBECONFIG=/etc/kubernetes/admin.conf
        until kubectl get nodes &> /dev/null; do sleep 2; done
        master_name=$(kubectl get nodes -o jsonpath='{.items[?(@.metadata.labels.node-role\.kubernetes\.io/control-plane=="")].metadata.name}')
        if [ -z "$master_name" ]; then
            echo "未找到control-plane节点!"
            exit 1
        fi
        # 获取污点信息
        taints=$(kubectl get node "$master_name" -o jsonpath='{.spec.taints}')
        if [ "$taints" = "null" ] || [ -z "$taints" ]; then
            echo "不存在污点！"
        else
            # 单独删除常见污点
            kubectl taint node --all "$master_name" node-role.kubernetes.io/control-plane- 2>/dev/null ||true
            kubectl taint node --all "$master_name" node-role.kubernetes.io/master- 2>/dev/null|| true
            kubectl taint node --all "$master_name" node.kubernetes.io/not-ready- 2>/dev/null || true
            echo -e "污点已经移除！"
        fi
        echo  -e "\033[44;37m master结点全部部署完成，请拷贝kubeadm join命令行备用 \033[0m"
    elif [ "$nodetype" == "worker" ];then
        echo  -e "\033[44;37m 请执行master配置完成后提示的kubeadm join命令行加入集群 \033[0m"
        echo  -e "\033[44;37m 执行kubeadm join命令加入master后，请执行以下命令以更新 kubelet 配置文件并重启kubelet服务 \033[0m"
        echo "sed -i \"\$ a\\\\$kubelet_insert_content\" /var/lib/kubelet/config.yaml"
        echo "systemctl restart kubelet"
    else
        echo "Please specify the 'nodetype' correctly through the '-n' option." && exit
    fi
fi
setup_kubectl_completion